#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include "Socket.h"

using namespace std;
using namespace yazi::socket;


Socket::Socket() : m_sockfd(0) {}

Socket::Socket(const string& ip, const int port)
    : m_ip(ip), m_port(port), m_sockfd(0) {}

Socket::~Socket(){
    close();
}

bool Socket::bind(){
    sockaddr_in sockaddr;
    memset(&sockaddr, 0, sizeof sockaddr);
    sockaddr.sin_family = AF_INET;
    if(m_ip == "") 
        sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    else 
        sockaddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
    sockaddr.sin_port = htons(m_port);
    int ret = ::bind(m_sockfd, (struct sockaddr*)&sockaddr, sizeof sockaddr);
    if(ret == -1){
        perror("bind()");
        return false;
    }
    return true;
}

bool Socket::listen(const int maxConnection){
    int ret = ::listen(m_sockfd, maxConnection);
    if(ret == -1){
        perror("listen()");
        return false;
    }
    return true;
}

int Socket::accept(){
    sockaddr_in raddr;
    memset(&raddr, 0, sizeof raddr);
    socklen_t raddr_len = sizeof raddr;
    int connfd = ::accept(m_sockfd, (struct sockaddr*)&raddr, &raddr_len);
    if(connfd < 0){
        perror("accept()");
        connfd = -1;
    }
    return connfd;
}

bool Socket::connect(const string& ip, const int port){
    sockaddr_in raddr;
    memset(&raddr, 0, sizeof raddr);
    raddr.sin_family = AF_INET;
    raddr.sin_addr.s_addr = inet_addr(ip.c_str());
    raddr.sin_port = htons(port);
    int ret = ::connect(m_sockfd, (struct sockaddr*)&raddr, sizeof raddr);
    if(ret == -1){
        perror("connect()");
        return false;
    }
    return true;
}

void Socket::close(){
    if(m_sockfd > 0){
        ::close(m_sockfd);
        m_sockfd = 0;
    }
}

int Socket::recv(char* buf, int len){
    int ret = ::recv(m_sockfd, buf, len, 0);
    return ret;
}


int Socket::send(const char* buf, int len){
    int ret = ::send(m_sockfd, buf, len, 0);
    return ret;
}

bool Socket::set_nonblock(){
    int file_state = fcntl(m_sockfd, F_GETFL, 0);
    file_state |= O_NONBLOCK;
    int ret = fcntl(m_sockfd, F_SETFL, file_state);
    if(ret == -1){
        perror("fcntl()");
        return false;
    }
    return true;
}

bool Socket::set_recv_buffer(const int buf_len){
    int optval = buf_len;
    int ret = ::setsockopt(m_sockfd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof optval);
    if (ret == -1)
    {
        perror("setsockopt()");
        return false;
    }
    return true;
}
bool Socket::set_send_buffer(const int buf_len){
    int optval = buf_len;
    int ret = ::setsockopt(m_sockfd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof optval);
    if (ret == -1)
    {
        perror("setsockopt()");
        return false;
    }
    return true;
}

bool Socket::set_linger(bool active, int seconds){
    struct linger l;
    l.l_onoff = (int)active;
    l.l_linger = seconds;

    int ret = setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, &l, sizeof l);
    if(ret == -1){
        perror("setsockopt()");
        return false;
    }
    return true;
}

bool Socket::set_keep_alive(){
    int optval = 1;
    int ret = ::setsockopt(m_sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval);
    if (ret == -1)
    {
        perror("setsockopt()");
        return false;
    }
    return true;
}
bool Socket::set_reuse_addr(){
    int optval = 1;
    int ret = ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
    if (ret == -1)
    {
        perror("setsockopt()");
        return false;
    }
    return true;
}

bool Socket::set_reuse_port(){
    int optval = 1;
    int ret = ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof optval);
    if(ret == -1){
        perror("setsockopt()");
        return false;
    }
    return true;
}


